﻿using System;
using System.Collections.Generic;
using ConceptsFramework.Containers.Global;

namespace ConceptsFramework.AbstractConceptElements
{
    public abstract class AbstractRelationship
    {
        public AbstractRelationship()
        {
            _ID = Guid.NewGuid().ToString();
        }

        private string _ID;
        public string ID
        {
            get
            {
                return _ID;
            }
        }
        
        public AbstractBindableConcept LeftConcept
        {
            get; set;
        }

        private List<AbstractBindableConcept> _RightConcepts;
        public List<AbstractBindableConcept> RightConcepts
        {
            get
            {
                if (_RightConcepts == null)
                    _RightConcepts = new List<AbstractBindableConcept>();
                return _RightConcepts;
            }
            set
            {
                _RightConcepts = value;
            }
        }

        public abstract string RelationshipSyntax { get; }


        /// <summary>
        /// Commit the current relationship, without computing the InverseRelationships.
        /// </summary>
        public void Commit()
        {
            GlobalRelationshipsSingletonContainer.Instance.Add(this);
            if (!LeftConcept.RelationshipsMap.Contains(this.ID))
                LeftConcept.RelationshipsMap.Add(this.ID);
        }

        /// <summary>
        /// Resolve all relationships for the related concepts, and add it in 
        /// the global relationship container
        /// </summary>
        /// <typeparam name="U">Relationship type</typeparam>
        /// <typeparam name="T">Inverse relationships type</typeparam>
        public void Commit<U, T>(bool InversedRelationshipsSolved)
            where U : AbstractRelationship, new()
            where T : AbstractRelationship, new()
        {       
            GlobalRelationshipsSingletonContainer.Instance.Add(this);
            if (!LeftConcept.RelationshipsMap.Contains(this.ID))
                LeftConcept.RelationshipsMap.Add(this.ID);
            
            if (!InversedRelationshipsSolved)
            {
                List<T> allInverseRelationships = getDefaultInverseRelationships<T>();
                foreach (T anInverseRelationShip in allInverseRelationships)
                    anInverseRelationShip.Commit<T, U>(true);
            }
        }

        /// <summary>
        /// Get the default Inverse relationships list for the current relationship.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        protected List<T> getDefaultInverseRelationships<T>() where T : AbstractRelationship, new()
        {
            List<T> inverseRelationships = new List<T>();
            foreach(AbstractBindableConcept aLeftConcept in this.RightConcepts)
            {
                T aNewInverseRelationship = new T();
                aNewInverseRelationship.LeftConcept = aLeftConcept;
                aNewInverseRelationship.RightConcepts = new List<AbstractBindableConcept>();
                aNewInverseRelationship.RightConcepts.Add(this.LeftConcept);
                inverseRelationships.Add(aNewInverseRelationship);
            }
            return inverseRelationships;
        }

        public abstract List<T> GetInverseRelationships<T>() where T : AbstractRelationship, new();
    }
}
